#include "global.h"

/**
 * @brief 初始化 USART 调试串口
 *
 * 该函数配置 USART 调试串口的 GPIO 和工作参数，并使能中断。
 */
void usartDebug_init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;

    // 打开串口 GPIO 的时钟
    DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_CLK, ENABLE);

    // 打开串口外设的时钟
    DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK, ENABLE);

    // 将 USART Tx 的 GPIO 配置为推挽复用模式
    GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_GPIO_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure);

    // 将 USART Rx 的 GPIO 配置为浮空输入模式
    GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);

    // 配置串口的工作参数
    USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE;          // 配置波特率
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;         // 配置数据字长
    USART_InitStructure.USART_StopBits = USART_StopBits_1;              // 配置停止位
    USART_InitStructure.USART_Parity = USART_Parity_No;                 // 配置校验位
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; // 配置硬件流控制
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;     // 配置工作模式，收发一起
    USART_Init(DEBUG_USARTx, &USART_InitStructure);                     // 完成串口的初始化配置

    // 串口中断优先级配置
    NVIC_InitTypeDef NVIC_InitStructure;
    NVIC_InitStructure.NVIC_IRQChannel = DEBUG_USART_IRQ;               // 配置 USART 中断源
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;           // 抢断优先级
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;                  // 子优先级
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                     // 使能中断
    NVIC_Init(&NVIC_InitStructure);                                     // 初始化配置 NVIC

    // 使能串口接收中断
    USART_ITConfig(DEBUG_USARTx, USART_IT_RXNE, ENABLE);

    // 使能串口
    USART_Cmd(DEBUG_USARTx, ENABLE);
}

/**
 * @brief 发送一个字节数据到 USART
 *
 * @param pUSARTx USART 外设指针
 * @param ch 要发送的字节数据
 */
void usartDebug_sendByte(USART_TypeDef *pUSARTx, uint8_t ch)
{
    // 发送一个字节数据到 USART
    USART_SendData(pUSARTx, ch);

    // 等待发送数据寄存器为空
    while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET)
        ;
}

/**
 * @brief 发送一个字节数组到 USART
 *
 * @param pUSARTx USART 外设指针
 * @param array 要发送的字节数组
 * @param num 数组长度
 */
void usartDebug_sendArray(USART_TypeDef *pUSARTx, uint8_t *array, uint16_t num)
{
    uint8_t i;

    for (i = 0; i < num; i++)
    {
        // 发送一个字节数据到 USART
        usartDebug_sendByte(pUSARTx, array[i]);
    }

    // 等待发送完成
    while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TC) == RESET)
        ;
}

/**
 * @brief 发送一个字符串到 USART
 *
 * @param pUSARTx USART 外设指针
 * @param str 要发送的字符串
 */
void usartDebug_sendString(USART_TypeDef *pUSARTx, char *str)
{
    unsigned int k = 0;

    do
    {
        usartDebug_sendByte(pUSARTx, *(str + k));
        k++;
    } while (*(str + k) != '\0');

    // 等待发送完成
    while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TC) == RESET)
        ;
}

/**
 * @brief 发送一个 16 位数据到 USART
 *
 * @param pUSARTx USART 外设指针
 * @param ch 要发送的 16 位数据
 */
void usartDebug_sendHalfWord(USART_TypeDef *pUSARTx, uint16_t ch)
{
    uint8_t temp_h, temp_l;

    // 取出高八位
    temp_h = (ch & 0XFF00) >> 8;
    // 取出低八位
    temp_l = ch & 0XFF;

    // 发送高八位
    USART_SendData(pUSARTx, temp_h);
    while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET)
        ;

    // 发送低八位
    USART_SendData(pUSARTx, temp_l);
    while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET)
        ;
}

/**
 * @brief 重定向 C 库函数 printf 到 USART
 *
 * 该函数使能使用 printf 函数通过 USART 输出调试信息。
 *
 * @param ch 要发送的字符
 * @param f 文件指针（未使用）
 * @return 发送的字符
 */
int fputc(int ch, FILE *f)
{
    // 发送一个字节数据到串口
    USART_SendData(DEBUG_USARTx, (uint8_t)ch);

    // 等待发送完毕
    while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_TXE) == RESET)
        ;

    return (ch);
}

/**
 * @brief 重定向 C 库函数 scanf 到 USART
 *
 * 该函数使能使用 scanf、getchar 等函数通过 USART 输入数据。
 *
 * @param f 文件指针（未使用）
 * @return 接收到的字符
 */
int fgetc(FILE *f)
{
    // 等待串口输入数据
    while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_RXNE) == RESET)
        ;

    return (int)USART_ReceiveData(DEBUG_USARTx);
}
